package api

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/go-playground/validator/v10"
	"github.com/golang-jwt/jwt/v5"
	"github.com/rongcloud/server-sdk-go/v3/sdk"
	"go.uber.org/zap"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"net/http"
	"project_api/user_web/forms"
	"project_api/user_web/global"
	"project_api/user_web/global/reponse"
	"project_api/user_web/middlewares"
	"project_api/user_web/models"
	"project_api/user_web/proto/user"
	"project_api/user_web/third_components"
	"project_api/user_web/utils"
	"strconv"
	"strings"
	"time"
)

func HandleGrpcErrorToHttp(err error, c *gin.Context) {
	//将grpc的code转换成http的状态码
	if err != nil {
		if e, ok := status.FromError(err); ok {
			switch e.Code() {
			case codes.NotFound:
				c.JSON(http.StatusNotFound, gin.H{
					"msg": e.Message(),
				})
			case codes.Internal:
				c.JSON(http.StatusInternalServerError, gin.H{
					"msg:": "内部错误",
				})
			case codes.InvalidArgument:
				c.JSON(http.StatusBadRequest, gin.H{
					"msg": "参数错误",
				})
			case codes.Unavailable:
				c.JSON(http.StatusInternalServerError, gin.H{
					"msg": "用户服务不可用",
				})
			default:
				c.JSON(http.StatusInternalServerError, gin.H{
					"msg": e.Code(),
				})
			}
			return
		}
	}
}

func GetUserList(ctx *gin.Context) {
	//// 1. 原始获取链接方式
	//userConn, err := grpc.NewClient(fmt.Sprintf("%s:%d", global.ServerConfig.UserSrvInfo.Host, global.ServerConfig.UserSrvInfo.Port), grpc.WithTransportCredentials(insecure.NewCredentials()))
	//if err != nil {
	//	zap.S().Errorw("[GetUserList] 连接 [用户服务失败]", "msg", err.Error())
	//	return
	//}
	//userSrvClient := user.NewUserClient(userConn)
	//listArr, err := userSrvClient.GetUserList(context.Background(), &user.PageInfo{
	//	Pn:    uint32(pnInt),
	//	PSize: uint32(psizeInt),
	//})

	// 2. 从consul获取服务方式

	// 获取前端参数
	pn := ctx.DefaultQuery("pn", "1")
	pnInt, _ := strconv.Atoi(pn)
	psize := ctx.DefaultQuery("psize", "5")
	psizeInt, _ := strconv.Atoi(psize)

	listArr, err := global.UserSrvClient.GetUserList(context.Background(), &user.PageInfo{
		Pn:    uint32(pnInt),
		PSize: uint32(psizeInt),
	})

	if err != nil {
		zap.S().Errorw("[GetUserList]查询用户列表失败", "msg", err.Error())
		HandleGrpcErrorToHttp(err, ctx)
		return
	}
	userId, _ := ctx.Get("userId")
	zap.S().Infof("访问用户: %d", userId)
	reMap := gin.H{
		"total": listArr.Total,
	}
	result := make([]interface{}, 0)
	for _, value := range listArr.Data {
		//另一种方式
		//data := make(map[string]interface{})
		//data["id"] = value.Id
		userRow := reponse.UserResponse{
			Id:       value.Id,
			NickName: value.NickName,
			//Birthday: time.Time(time.Unix(int64(value.BirthDay), 0)).Format("2006-01-02"),
			Birthday:   value.BirthDay,
			Sex:        value.Sex,
			Phone:      value.Phone,
			HeadImgUrl: utils.AddOssPrefix(value.HeadImgUrl),
		}
		result = append(result, userRow)
	}

	reMap["data"] = result
	ctx.JSON(http.StatusOK, reMap)
}

func GetUserInfo(ctx *gin.Context) {
	// 获取前端参数
	userInfoFrom := forms.UserInfoForm{}
	if err := ctx.ShouldBind(&userInfoFrom); err != nil {
		utils.CommonResponse(ctx, 0, "参数错误", nil)
		return
	}

	userRow, _ := global.UserSrvClient.GetUserById(context.Background(), &user.IdRequest{
		Id: userInfoFrom.UserId,
	})
	if userRow == nil {
		utils.CommonResponse(ctx, 0, "用户不存在", nil)
		return
	}

	parsedTime, _ := time.Parse(time.RFC3339, userRow.CreatedAt)
	userResData := gin.H{
		"id":            userRow.Id,
		"nickname":      userRow.NickName,
		"headimgurl":    utils.AddOssPrefix(userRow.HeadImgUrl),
		"phone":         userRow.Phone,
		"created_at":    parsedTime.Format("2006-01-02 15:04:05"),
		"sex":           userRow.Sex,
		"birthday":      userRow.BirthDay,
		"constellation": userRow.Constellation,
		"city":          userRow.City,
		"mizuan":        userRow.MiZuan,
		"mibi":          userRow.MiBi,
		"is_idcard":     userRow.IsIdCard,
		"ry_uid":        userRow.RyUid,
		"is_sign":       userRow.IsSign,
		"is_accept":     userRow.IsAccept,
		"isopenorder":   0,
	}

	imgAndLevel := utils.GetVipLevelAndImg(userRow.Id, 3)
	vipImg, _ := imgAndLevel["vip_img"].(string)
	userResData["vip_level"] = imgAndLevel["vip_level"]
	userResData["vip_img"] = utils.AddOssPrefix(vipImg)
	userResData["follows_num"] = getUserFollowCount(userInfoFrom.UserId)
	userResData["fans_num"] = getUserFansCount(userInfoFrom.UserId)

	userResData["acceptnewcorner"] = getFlagCount(ctx, "accept:user:mark", userInfoFrom.UserId)
	userResData["ordernewcorner"] = getFlagCount(ctx, "order:user:mark", userInfoFrom.UserId)
	userResData["able_send_letter_img"] = 0 // 不能发私信图

	currentTime := time.Now()
	// 判断用户注册时间是否不超过一天
	if parsedTime.Add(24 * time.Hour).After(currentTime) {
		userResData["able_send_letter_img"] = 1 // 不能发私信图
	}

	userResData["able_send_screen_img"] = 0 // 不能发公屏图
	userResData["is_show_service"] = 0      // 不能发公屏图
	userResData["logout_info"] = map[string]interface{}{}
	var logoutApply models.LogoutApply
	result := global.DB.Where(&models.LogoutApply{UserId: userInfoFrom.UserId, Status: models.StatusDefault}).First(&logoutApply)
	if result.RowsAffected > 0 {
		//t := time.Unix(logoutApply.ApplyTime, 0)
		//applyTimeStr := t.Format("2006-01-02 15:04:05")
		parsedLogoutTime, _ := time.Parse(time.RFC3339, logoutApply.LogoutTime)
		userResData["logout_info"] = map[string]interface{}{
			"id":          logoutApply.ID,
			"user_id":     logoutApply.UserId,
			"apply_time":  logoutApply.ApplyTime,
			"logout_time": parsedLogoutTime.Format("2006-01-02 15:04:05"),
		}
	}

	utils.CommonResponse(ctx, 1, "success", userResData)
}

func getFlagCount(ctx *gin.Context, flag string, userId int32) int32 {
	if userId == 0 || flag == "" {
		return 0
	}
	// 获取 order:user:mark:$user_id
	orderNewCorner := 0
	orderNewCornerArr, _ := global.RedisDb.HGetAll(ctx, fmt.Sprintf("%s:%d", flag, userId)).Result()
	for _, v := range orderNewCornerArr {
		var val int
		fmt.Sscanf(v, "%d", &val)
		orderNewCorner += val
	}
	return int32(orderNewCorner)
}

func getUserFansCount(userId int32) int64 {
	var fansCount int64
	if userId == 0 {
		return fansCount
	}
	global.DB.Model(&models.Follows{}).
		Joins("LEFT JOIN b_users ON b_follows.user_id = b_users.id").
		Where("b_follows.followed_user_id = ? AND b_follows.status = ? AND b_users.status = ? AND b_users.zhuxiao_phone = ? AND b_users.phone <> ?",
			userId, 1, 1, "", "").
		Count(&fansCount)
	return fansCount
}

func getUserFollowCount(userId int32) int64 {
	var followCount int64
	if userId == 0 {
		return followCount
	}
	global.DB.Model(&models.Follows{}).
		Joins("LEFT JOIN b_users ON b_follows.followed_user_id = b_users.id").
		Where("b_follows.user_id = ? AND b_follows.status = ? AND b_users.status = ? AND b_users.zhuxiao_phone = ? AND b_users.phone <> ?",
			userId, 1, 1, "", "").
		Count(&followCount)
	return followCount
}

func PasswordLogin(ctx *gin.Context) {
	// 表单验证
	passwordLoginFrom := forms.PassWordLoginForm{}
	if err := ctx.ShouldBind(&passwordLoginFrom); err != nil {
		zap.S().Errorw("[PasswordLogin] 表单验证失败", "msg", err.Error())
		utils.CommonResponse(ctx, 0, "参数错误", nil)
		return
	}

	// 登录的业务
	userRow, err := global.UserSrvClient.GetUserByMobile(ctx, &user.PhoneRequest{
		Phone: passwordLoginFrom.Phone,
	})
	if userRow == nil {
		utils.CommonResponse(ctx, 0, "用户不存在", nil)
		return
	}
	if userRow.Status != 1 {
		utils.CommonResponse(ctx, 0, "用户不可用", nil)
		return
	}
	checkRes, err := global.UserSrvClient.CheckPassWord(ctx, &user.PasswordCheckInfo{
		Password:          passwordLoginFrom.PassWord,
		Salt:              userRow.Salt,
		EncryptedPassword: userRow.Pass,
	})
	if err != nil {
		zap.S().Errorw("[PasswordLogin] UserSrvClient error", "msg", err.Error())
		return
	}

	if !checkRes.Success {
		utils.CommonResponse(ctx, 0, "密码错误", nil)
		return
	}

	deviceId := ctx.GetHeader("deviceid")
	// 检查黑名单
	if deviceId != "" {
		isMember, _ := global.RedisDb.SIsMember(ctx, "black:device", deviceId).Result()
		if isMember {
			go insertBlackDeviceTry(ctx, passwordLoginFrom.Phone)
			utils.CommonResponse(ctx, 0, "验证码错误或已过期1", nil)
			return
		}
	}

	clientIp := utils.GetClientIP(ctx)
	if clientIp != "" {
		isMember, _ := global.RedisDb.SIsMember(ctx, "black:ip", clientIp).Result()
		if isMember {
			go insertBlackIpTry(ctx, passwordLoginFrom.Phone, clientIp)
			utils.CommonResponse(ctx, 0, "无效请求2", nil)
			return
		}
	}

	global.DB.Model(&models.Users{}).Where(&models.Users{ID: userRow.Id}).Updates(&models.Users{
		LoginIp: clientIp,
	})

	go insertUserDevice(ctx, userRow.Id)

	userResData, err := getUserLoginInfo(ctx, userRow.Id, userRow.RyToken, userRow.RyUid, userRow)
	if err != nil {
		utils.CommonResponse(ctx, 0, "获取用户token错误", nil)
		return
	}

	utils.CommonResponse(ctx, 1, "success", userResData)
}

func SendPasswordSms(ctx *gin.Context) {
	// 表单验证
	smsSendForm := forms.SmsSendForm{}
	if err := ctx.ShouldBind(&smsSendForm); err != nil {
		utils.CommonResponse(ctx, 0, "参数错误", nil)
		return
	}
	userId, _ := ctx.Get("userId")
	var userRow models.Users
	if err := global.DB.Where(&models.Users{ID: userId.(int32)}).First(&userRow).Error; err != nil {
		utils.CommonResponse(ctx, 0, "用户不存在", nil)
		return
	}
	if userRow.Status != 1 {
		utils.CommonResponse(ctx, 0, "用户不可用", nil)
		return
	}
	if userRow.Phone != smsSendForm.Phone {
		utils.CommonResponse(ctx, 0, "手机号不匹配", nil)
		return
	}

	setCacheStr, err := global.RedisDb.Get(ctx, fmt.Sprintf("setPasswordPhoneCode:%s", smsSendForm.Phone)).Result()
	if setCacheStr != "" {
		utils.CommonResponse(ctx, 0, "请5分钟后再发送验证码", nil)
		return
	}

	// 根据手机号检查短信发送次数
	phoneLimitCount := checkSmsLimit(ctx, smsSendForm.Phone)
	if phoneLimitCount != 0 {
		surplusTimeStr := utils.Sec2Time(int64(phoneLimitCount))
		msg := fmt.Sprintf("操作频繁%s后重试", surplusTimeStr)
		utils.CommonResponse(ctx, 0, msg, gin.H{
			"surplus_time": surplusTimeStr,
		})
		return
	}

	// 根据ip检查短信发送次数
	ipLimitCount := checkIpLimit(ctx)
	if ipLimitCount != 0 {
		surplusTimeStr := utils.Sec2Time(int64(ipLimitCount))
		msg := fmt.Sprintf("操作频繁%s后重试1", surplusTimeStr)
		utils.CommonResponse(ctx, 0, msg, gin.H{
			"surplus_time": surplusTimeStr,
		})
		return
	}

	code := utils.GenerateSmsCode(4)
	sendSmsRes := utils.SendSmsPart(ctx, smsSendForm.Phone, code)
	if !sendSmsRes {
		utils.CommonResponse(ctx, 0, "短信发送失败1", nil)
		return
	}

	var codeModel models.Code
	global.DB.Where(&models.Code{Phone: smsSendForm.Phone}).First(&codeModel)

	_, err = global.RedisDb.SetEX(ctx, fmt.Sprintf("setPasswordPhoneCode:%s", smsSendForm.Phone), code, time.Minute*5).Result()
	if err != nil {
		zap.S().Errorw("[SendPasswordSms] redis", "msg", err.Error())
		utils.CommonResponse(ctx, 0, "服务繁忙，请稍后再试！", nil)
		return
	}

	saveDbRes := saveCodeToDb(ctx, smsSendForm.Phone, code, &codeModel)
	if !saveDbRes {
		utils.CommonResponse(ctx, 0, "短信发送失败2", nil)
		return
	}
	utils.CommonResponse(ctx, 1, "短信发送成功", nil)
}

func PasswordPhoneCheck(ctx *gin.Context) {
	smsLoginForm := forms.SmsLoginForm{}
	if err := ctx.ShouldBind(&smsLoginForm); err != nil {
		utils.CommonResponse(ctx, 0, "参数错误", nil)
		return
	}

	// Post 获取值
	phone := smsLoginForm.Phone
	userId, _ := ctx.Get("userId")
	var userRow models.Users
	userResult := global.DB.Where(&models.Users{ID: userId.(int32)}).First(&userRow)
	if userResult.Error != nil {
		utils.CommonResponse(ctx, 0, "用户不存在1", nil)
		return
	}
	if userResult.RowsAffected == 0 {
		utils.CommonResponse(ctx, 0, "用户不存在2", nil)
		return
	}
	if userRow.Status != 1 {
		utils.CommonResponse(ctx, 0, "用户不可用", nil)
		return
	}

	codeCacheKey := fmt.Sprintf("setPasswordPhoneCode:%s", phone)
	setCacheStr, _ := global.RedisDb.Get(ctx, codeCacheKey).Result()
	if setCacheStr == "" {
		utils.CommonResponse(ctx, 0, "验证码已过期请重新获取", nil)
		return
	}
	if setCacheStr != smsLoginForm.Code {
		utils.CommonResponse(ctx, 0, "验证码不正确", nil)
		return
	}
	_, err := global.RedisDb.Del(ctx, codeCacheKey).Result()
	if err != nil {
		zap.S().Errorw("[PasswordPhoneCheck] redis", "msg", err.Error())
		utils.CommonResponse(ctx, 0, "服务内部错误！", nil)
		return
	}
	utils.CommonResponse(ctx, 1, "手机验证码校验成功！", nil)
}

func SetPassword(ctx *gin.Context) {
	setPasswordForm := forms.SetPasswordForm{}
	if err := ctx.ShouldBind(&setPasswordForm); err != nil {
		utils.CommonResponse(ctx, 0, "参数错误", nil)
		return
	}

	userId, _ := ctx.Get("userId")
	var userRow models.Users
	userResult := global.DB.Where(&models.Users{ID: userId.(int32)}).First(&userRow)
	if userResult.Error != nil {
		utils.CommonResponse(ctx, 0, "用户不存在1", nil)
		return
	}
	if userResult.RowsAffected == 0 {
		utils.CommonResponse(ctx, 0, "用户不存在2", nil)
		return
	}
	if userRow.Status != 1 {
		utils.CommonResponse(ctx, 0, "用户不可用", nil)
		return
	}

	_, err := global.UserSrvClient.SetUserPassword(context.Background(), &user.SetUserPassInfo{
		Id:   userRow.ID,
		Pass: setPasswordForm.Password,
	})
	if err != nil {
		utils.CommonResponse(ctx, 0, "修改失败", nil)
		return
	}

	utils.CommonResponse(ctx, 1, "修改成功！", nil)
}

func JwtLogin(ctx *gin.Context) {
	// 表单验证
	passwordLoginFrom := forms.PassWordLoginOldForm{}
	if err := ctx.ShouldBind(&passwordLoginFrom); err != nil {
		zap.S().Errorw("[PasswordLogin] 表单验证失败", "msg", err.Error())
		HandleValidatorError(ctx, err)
		return
	}

	if !store.Verify(passwordLoginFrom.CaptchaId, passwordLoginFrom.Captcha, false) {
		ctx.JSON(http.StatusBadRequest, gin.H{
			"msg": "验证码错误",
		})
		return
	}

	//userConn, err := grpc.NewClient(fmt.Sprintf("%s:%d", global.ServerConfig.UserSrvInfo.Host, global.ServerConfig.UserSrvInfo.Port), grpc.WithTransportCredentials(insecure.NewCredentials()))
	//if err != nil {
	//	zap.S().Errorw("[PasswordLogin] 连接 [用户服务失败]", "msg", err.Error())
	//	return
	//}
	//userSrvClient := user.NewUserClient(userConn)
	// 登录的业务
	userRow, err := global.UserSrvClient.GetUserByMobile(context.Background(), &user.PhoneRequest{
		Phone: passwordLoginFrom.Mobile,
	})
	if err != nil {
		if e, ok := status.FromError(err); ok {
			switch e.Code() {
			case codes.NotFound:
				ctx.JSON(http.StatusBadRequest, map[string]string{
					"mobile": "用户不存在",
				})
			default:
				ctx.JSON(http.StatusInternalServerError, map[string]string{
					"mobile": "登录失败",
				})
			}
			return
		}
	}

	checkRes, err := global.UserSrvClient.CheckPassWord(context.Background(), &user.PasswordCheckInfo{
		Password:          passwordLoginFrom.PassWord,
		EncryptedPassword: userRow.Pass,
	})
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"mobile": "服务错误",
		})
		return
	}

	if !checkRes.Success {
		ctx.JSON(http.StatusBadRequest, gin.H{
			"msg": "密码错误",
		})
		return
	}

	j := middlewares.NewJWT()
	claims := models.CustomClaims{
		ID:       uint(userRow.Id),
		NickName: userRow.NickName,
		//AuthorityId: uint(userRow.Role),
		RegisteredClaims: jwt.RegisteredClaims{
			NotBefore: jwt.NewNumericDate(time.Now()),
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24 * 30)),
			IssuedAt:  jwt.NewNumericDate(time.Now()),
			Issuer:    "shiguangyuyin",
		},
	}

	token, err := j.CreateToken(claims)
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"msg": "生成token失败",
		})
		return
	}

	ctx.JSON(http.StatusOK, gin.H{
		"id":         userRow.Id,
		"nick_name":  userRow.NickName,
		"token":      token,
		"expired_at": time.Now().Unix() + 60*60*24*30,
	})
}

func removeTopStruct(fileds map[string]string) map[string]string {
	rsp := map[string]string{}
	for field, err := range fileds {
		rsp[field[strings.Index(field, ".")+1:]] = err
	}
	return rsp
}

func HandleValidatorError(c *gin.Context, err error) {
	var errs validator.ValidationErrors
	ok := errors.As(err, &errs)
	if !ok {
		c.JSON(http.StatusOK, gin.H{
			"msg": err.Error(),
		})
	}
	c.JSON(http.StatusBadRequest, gin.H{
		"error": removeTopStruct(errs.Translate(global.Trans)),
	})
	return
}

func OneClickReg(ctx *gin.Context) {
	// 表单验证
	oneClickForm := forms.OneClickRegForm{}
	if err := ctx.ShouldBind(&oneClickForm); err != nil {
		utils.CommonResponse(ctx, 0, "参数错误", nil)
		return
	}

	// Post 获取值
	var headImgUrl string
	if oneClickForm.HeadImgUrl != "" {
		aliOssInfo := global.ServerConfig.AliOssInfo
		headImgUrl, _ = third_components.UploadBase64Image(aliOssInfo.AccessKey, aliOssInfo.AccessSecret, aliOssInfo.Endpoint, aliOssInfo.BucketName, oneClickForm.HeadImgUrl, 1)
	}

	if headImgUrl == "" {
		headImgUrl = utils.AddOssPrefix("wares/1e051a4df251a8bf1fdc975abacbd836.png")
		if oneClickForm.Sex == 2 {
			headImgUrl = utils.AddOssPrefix("wares/dcaf9e120abd944ecfd522b8da32b8f1.png")
		}
	}

	phone, _ := global.RedisDb.Get(ctx.Request.Context(), fmt.Sprintf("oneClickLogin:%s", oneClickForm.CToken)).Result()
	if phone == "" {
		utils.CommonResponse(ctx, 0, "已过期", nil)
		return
	}

	currTime := time.Now()
	currFormatTime := currTime.Format("2006-01-02 15:04:05")
	lock, err := utils.AcquireLock(ctx, phone, currFormatTime, time.Duration(10))
	if err != nil {
		zap.S().Errorw("[OneClickReg] redis", "msg", err.Error())
		return
	}
	if !lock {
		utils.CommonResponse(ctx, 0, "操作频繁", nil)
		return
	}

	deviceId := ctx.GetHeader("deviceid")
	userId := int32(0)
	userRow, err := global.UserSrvClient.GetUserByMobile(context.Background(), &user.PhoneRequest{Phone: phone})
	if userRow != nil {
		utils.CommonResponse(ctx, 0, "用户已经存在", nil)
		return
	}

	// 检查黑名单
	if deviceId != "" {
		isMember, _ := global.RedisDb.SIsMember(ctx.Request.Context(), "black:device", deviceId).Result()
		if isMember {
			go insertBlackDeviceTry(ctx, phone)
			utils.CommonResponse(ctx, 0, "无效请求1", nil)
			return
		}
	}

	clientIp := utils.GetClientIP(ctx)
	if clientIp != "" {
		isMember, _ := global.RedisDb.SIsMember(ctx.Request.Context(), "black:ip", clientIp).Result()
		if isMember {
			go insertBlackIpTry(ctx, phone, clientIp)
			utils.CommonResponse(ctx, 0, "无效请求2", nil)
			return
		}
	}

	maxCount := 10
	deviceCount, err := global.RedisDb.HIncrBy(ctx.Request.Context(), "registered:device", deviceId, 1).Result()
	if err != nil {
		zap.S().Errorw("[OneClickReg] redis1", "msg", err.Error())
		return
	}
	if deviceCount > int64(maxCount) {
		utils.CommonResponse(ctx, 0, fmt.Sprintf("同一个设备不能注册超过%d个账号", maxCount), nil)
		return
	}

	go insertAccessLog(ctx, "v1/api/OneClickReg", userId)
	go insertRegisterTry(ctx, phone, "v1/api/OneClickReg")

	parsedTime, err := time.Parse("2006-01-02", oneClickForm.Birthday)
	if err != nil {
		parsedTime = time.Now()
	}
	birthdayTm := uint64(parsedTime.Unix())

	checkWordsRes := checkIllegalWords(ctx, oneClickForm.NickName)
	if checkWordsRes {
		utils.CommonResponse(ctx, 0, "昵称含有敏感词", nil)
		return
	}

	var constellation string
	constellationResult, _ := utils.GetBirthdayMsg(oneClickForm.Birthday, 2)
	// 类型断言，将返回值转换为字符串类型
	if constellationI, ok := constellationResult.(string); ok {
		// 成功转换为字符串
		constellation = constellationI
	}

	createUser, err := global.UserSrvClient.CreateUser(ctx, &user.CreateUserInfo{
		NickName:      oneClickForm.NickName,
		Phone:         phone,
		Sex:           oneClickForm.Sex,
		BirthDay:      birthdayTm,
		Constellation: constellation,
		HeadImgUrl:    headImgUrl,
		LoginIp:       clientIp,
		System:        ctx.GetHeader("system"),
		Channel:       ctx.GetHeader("chanel"),
		Appid:         utils.ParseHeaderInt(ctx, "appid"),
	})
	if err != nil {
		utils.CommonResponse(ctx, 0, "创建用户错误", nil)
		return
	}

	ryRow, err := addUserToRy(ctx, createUser.Id, createUser.NickName, headImgUrl)
	if err != nil {
		zap.S().Errorw("[LoginCodeV1] Error Rong Cloud:", "msg", err.Error())
		utils.CommonResponse(ctx, 0, "创建用户错误1", nil)
		return
	}

	var deviceSqlCount int64
	global.DB.Model(&models.UserDevice{}).Where(&models.UserDevice{DeviceId: ctx.GetHeader("deviceId")}).Count(&deviceSqlCount)
	isNew := 0
	if deviceSqlCount <= 1 {
		isNew = 1
	}

	global.DB.Model(&models.Users{}).Where(&models.Users{ID: createUser.Id}).Updates(&models.Users{
		RyToken:   ryRow.Token,
		RyUid:     ryRow.UserID,
		AliAvatar: "",
		RegOaid:   ctx.GetHeader("oaid"),
		IsNew:     int32(isNew),
	})

	insertUserDevice(ctx, createUser.Id)
	insertUserTotal(ctx, createUser.Id)

	userResData, err := getUserLoginInfo(ctx, createUser.Id, ryRow.Token, ryRow.UserID, createUser)
	if err != nil {
		utils.CommonResponse(ctx, 0, "获取用户token错误", nil)
		return
	}

	userResData["oaid_upload_res"] = []interface{}{}
	go insertDynamics(ctx, createUser.Id, createUser.NickName)
	utils.CommonResponse(ctx, 1, "success", userResData)
}

func LoginCodeV1(ctx *gin.Context) {
	// 表单验证
	smsLoginForm := forms.SmsLoginForm{}
	if err := ctx.ShouldBind(&smsLoginForm); err != nil {
		utils.CommonResponse(ctx, 0, "参数错误", nil)
		return
	}

	// Post 获取值
	phone := smsLoginForm.Phone
	deviceId := ctx.GetHeader("deviceid")
	userId := int32(0)
	userRow, err := global.UserSrvClient.GetUserByMobile(context.Background(), &user.PhoneRequest{Phone: phone})
	if err == nil {
		userId = userRow.Id
	}
	// 检查黑名单
	if deviceId != "" {
		isMember, _ := global.RedisDb.SIsMember(ctx.Request.Context(), "black:device", deviceId).Result()
		if isMember {
			go insertBlackDeviceTry(ctx, phone)
			utils.CommonResponse(ctx, 0, "验证码错误或已过期1", nil)
			return
		}
	}

	// 检查ip
	clientIp := utils.GetClientIP(ctx)
	if clientIp != "" {
		isMember, _ := global.RedisDb.SIsMember(ctx.Request.Context(), "black:ip", clientIp).Result()
		if isMember {
			go insertBlackIpTry(ctx, phone, clientIp)
			utils.CommonResponse(ctx, 0, "验证码错误或已过期2", nil)
			return
		}
	}

	go insertAccessLog(ctx, "v1/api/LoginCodeV1", userId)
	go insertRegisterTry(ctx, phone, "v1/api/LoginCodeV1")
	isCancelUser := isCancelUserByPhone(phone)
	if isCancelUser {
		utils.CommonResponse(ctx, 0, "验证码错误或已过期3", nil)
		return
	}
	if userRow != nil && userRow.Status != 1 {
		utils.CommonResponse(ctx, 0, "验证码错误或已过期4", nil)
		return
	}

	inArrRes := utils.InArrayByString(smsLoginForm.Phone, []string{"18810758459", "13011056162", "18810758461", "13283338399", "18935059432", "13141217195"})
	if !inArrRes {
		var codeModel models.Code
		result := global.DB.Where(&models.Code{Phone: smsLoginForm.Phone}).First(&codeModel)
		if result.Error != nil {
			utils.CommonResponse(ctx, 0, "验证码错误或已过期5", nil)
			return
		}
		if result.RowsAffected == 0 {
			utils.CommonResponse(ctx, 0, "验证码错误或已过期6", nil)
			return
		}
		sjc := codeModel.AddTime + 300 - time.Now().Unix()
		if sjc < 0 || codeModel.Code != smsLoginForm.Code {
			utils.CommonResponse(ctx, 0, "验证码错误或已过期7", nil)
			return
		}
	}

	if userRow == nil {
		cToken := utils.GetRandomStr(42, false)
		global.RedisDb.SetEX(ctx.Request.Context(), fmt.Sprintf("oneClickLogin:%s", cToken), phone, time.Duration(900))
		utils.CommonResponse(ctx, 1009, "", gin.H{
			"c_token": cToken,
		})
		return
	}

	ryRow, err := addUserToRy(ctx, userRow.Id, userRow.NickName, userRow.HeadImgUrl)
	if err != nil {
		zap.S().Errorw("[LoginCodeV1] Error Rong Cloud:", "msg", err.Error())
		utils.CommonResponse(ctx, 0, "验证码错误或已过期8", nil)
		return
	}
	global.DB.Model(&models.Users{}).Where(&models.Users{ID: userRow.Id}).Updates(&models.Users{
		RyToken: ryRow.Token,
		RyUid:   ryRow.UserID,
	})

	global.DB.Model(&models.Users{}).Where(&models.Users{ID: userRow.Id}).Updates(&models.Users{
		LoginIp: clientIp,
	})

	go insertUserDevice(ctx, userId)

	userResData, err := getUserLoginInfo(ctx, userId, ryRow.Token, ryRow.UserID, userRow)
	if err != nil {
		utils.CommonResponse(ctx, 0, "获取用户token错误", nil)
		return
	}

	utils.CommonResponse(ctx, 1, "成功", userResData)
}

func getUserLoginInfo(ctx *gin.Context, userId int32, ryToken string, ryUid string, userRow *user.UserInfoResponse) (gin.H, error) {
	// 设置token
	uToken := utils.GetRandomStr(40, false)
	third_components.SetToken(uToken, userId, 0)

	userResData := gin.H{
		"id":         userId,
		"nickname":   userRow.NickName,
		"headimgurl": utils.AddOssPrefix(userRow.HeadImgUrl),
		"ry_uid":     ryUid,
		"ry_token":   ryToken,
		"phone":      userRow.Phone,
		"channel":    "",
		"sex":        userRow.Sex,
		"sq_disable": userRow.SqDisable,
		"sj_disable": userRow.SjDisable,
		"is_idcard":  userRow.IsIdCard,
	}

	tokenArr, err := third_components.GetToken(uToken)
	if err != nil {
		return userResData, err
	}

	for tKey, tValue := range tokenArr {
		userResData[tKey] = tValue
	}
	return userResData, nil
}

// SendSmsCodeV1 验证码登录
func SendSmsCodeV1(ctx *gin.Context) {
	// 表单验证
	smsSendForm := forms.SmsSendForm{}
	if err := ctx.ShouldBind(&smsSendForm); err != nil {
		utils.CommonResponse(ctx, 0, "参数错误", nil)
		return
	}

	// 根据手机号检查短信发送次数
	phoneLimitCount := checkSmsLimit(ctx, smsSendForm.Phone)
	if phoneLimitCount != 0 {
		surplusTimeStr := utils.Sec2Time(int64(phoneLimitCount))
		msg := fmt.Sprintf("操作频繁%s后重试", surplusTimeStr)
		utils.CommonResponse(ctx, 0, msg, gin.H{
			"surplus_time": surplusTimeStr,
		})
		return
	}

	// 根据ip检查短信发送次数
	ipLimitCount := checkIpLimit(ctx)
	if ipLimitCount != 0 {
		surplusTimeStr := utils.Sec2Time(int64(ipLimitCount))
		msg := fmt.Sprintf("操作频繁%s后重试1", surplusTimeStr)
		utils.CommonResponse(ctx, 0, msg, gin.H{
			"surplus_time": surplusTimeStr,
		})
		return
	}

	// Post 获取值
	phone := smsSendForm.Phone
	deviceId := ctx.GetHeader("deviceid")
	userId := int32(0)
	userRow, err := global.UserSrvClient.GetUserByMobile(context.Background(), &user.PhoneRequest{Phone: phone})
	if err == nil {
		userId = userRow.Id
	}

	// 检查黑名单
	if deviceId != "" {
		isMember, _ := global.RedisDb.SIsMember(ctx.Request.Context(), "black:device", deviceId).Result()
		if isMember {
			go insertBlackDeviceTry(ctx, phone)
			utils.CommonResponse(ctx, 0, "短信发送成功1", gin.H{
				"surplus_time": 60,
			})
			return
		}
	}

	go insertAccessLog(ctx, "v1/api/sendSmsCodeV1", userId)
	go insertRegisterTry(ctx, phone, "v1/api/sendSmsCodeV1")
	isCancelUser := isCancelUserByPhone(phone)
	if isCancelUser {
		utils.CommonResponse(ctx, 0, "短信发送成功2", gin.H{
			"surplus_time": 60,
		})
		return
	}
	if userRow != nil && userRow.Status != 1 {
		utils.CommonResponse(ctx, 0, "短信发送成功3", gin.H{
			"surplus_time": 60,
		})
		return
	}

	var codeModel models.Code
	result := global.DB.Where(&models.Code{Phone: smsSendForm.Phone}).First(&codeModel)
	if result.RowsAffected > 0 {
		sjc := codeModel.AddTime + 120 - time.Now().Unix()
		if sjc > 0 {
			utils.CommonResponse(ctx, 0, fmt.Sprintf("请%d秒之后再试", sjc), gin.H{
				"surplus_time": sjc,
			})
			return
		}
	}

	code := utils.GenerateSmsCode(4)
	sendSmsRes := utils.SendSmsPart(ctx, phone, code)
	if !sendSmsRes {
		utils.CommonResponse(ctx, 0, "短信发送失败1", gin.H{
			"surplus_time": 60,
		})
		return
	}

	saveDbRes := saveCodeToDb(ctx, phone, code, &codeModel)
	if !saveDbRes {
		utils.CommonResponse(ctx, 0, "短信发送失败2", gin.H{
			"surplus_time": 60,
		})
		return
	}
	utils.CommonResponse(ctx, 1, "短信发送成功", gin.H{
		"surplus_time": 60,
	})
}

func isCancelUserByPhone(phone string) bool {
	var cancelTime string // 用来存储查询结果
	_ = global.DB.Model(&models.Users{}).Where(&models.Users{ZhuxiaoPhone: phone}).Order("zhuxiao_time DESC").Select("zhuxiao_time").Scan(&cancelTime).Error
	if cancelTime != "" {
		cancelTimeParsed, _ := time.Parse(time.RFC3339, cancelTime)
		// 转换为本地时间，忽略时区
		cancelTimeParsed = cancelTimeParsed.Local()
		// 获取当前时间
		currentTime := time.Now().Local() // 获取本地时间
		if cancelTimeParsed.Add(24 * time.Hour).After(currentTime) {
			return true
		}
	}
	return false
}

// $phone_user_id = DB::name('users')
// ->where(['phone' => $phone])
// ->value('id');
func insertAccessLog(ctx *gin.Context, requestPath string, userId int32) bool {
	currTime := time.Now()
	formattedTime := currTime.Format("2006-01-02 15:04:05")

	// 获取 Header
	accessLogModel := models.AccessLog{
		UserId:      userId,
		Appid:       utils.ParseHeaderInt(ctx, "appid"),
		System:      ctx.GetHeader("system"),
		Version:     ctx.GetHeader("version"),
		Channel:     ctx.GetHeader("channel"),
		DeviceId:    ctx.GetHeader("deviceId"),
		Host:        ctx.GetHeader("host"),
		UserAgent:   ctx.GetHeader("User_Agent"),
		Package:     ctx.GetHeader("package"),
		Oaid:        ctx.GetHeader("oaid"),
		Iaa:         utils.ParseHeaderInt(ctx, "iaa"),
		CreatedAt:   formattedTime,
		RequestData: utils.GetAllParams(ctx),
		Path:        requestPath,
	}

	global.DB.Create(&accessLogModel)
	userIpDeviceModel := models.UserIpDevice{
		UserId:    userId,
		Iaa:       utils.ParseHeaderInt(ctx, "iaa"),
		DeviceId:  ctx.GetHeader("deviceId"),
		Ip:        utils.GetClientIP(ctx),
		Oaid:      ctx.GetHeader("oaid"),
		CreatedAt: formattedTime,
	}
	global.DB.Create(&userIpDeviceModel)
	return true
}

func insertRegisterTry(ctx *gin.Context, phone string, requestPath string) bool {
	// 获取 Header
	registerTryModel := models.RegisterTry{
		Phone:    phone,
		DeviceId: ctx.GetHeader("deviceId"),
		Ip:       utils.GetClientIP(ctx),
		FromType: requestPath,
	}

	global.DB.Create(&registerTryModel)
	return true
}

func insertBlackDeviceTry(ctx *gin.Context, phone string) bool {
	// 获取 Header
	bdtModel := models.BlackDeviceTry{
		Phone:    phone,
		DeviceId: ctx.GetHeader("deviceId"),
	}

	global.DB.Create(&bdtModel)
	return true
}

func insertBlackIpTry(ctx *gin.Context, phone string, ip string) bool {
	// 获取 Header
	biModel := models.BlackIpTry{
		Ip:    ip,
		Phone: phone,
	}

	global.DB.Create(&biModel)
	return true
}

func checkSmsLimit(ctx *gin.Context, phone string) int {
	countKey := fmt.Sprintf("phone:sms:count:%s", phone)
	count, err := global.RedisDb.Incr(ctx.Request.Context(), countKey).Result()
	if err != nil {
		zap.S().Errorw("[checkCount] Error incrementing counter:", "msg", err.Error())
		return 0
	}
	if count >= 100 {
		ttl, err := global.RedisDb.TTL(ctx.Request.Context(), countKey).Result()
		if err != nil {
			zap.S().Errorw("[checkCount] Error ttl:", "msg", err.Error())
			return 0
		}

		if ttl == -1 {
			// If no TTL, set the expiration time to 12 hours
			err := global.RedisDb.Expire(ctx.Request.Context(), countKey, 12*time.Hour).Err()
			if err != nil {
				zap.S().Errorw("[checkCount] Error ttl expire:", "msg", err.Error())
				return 0
			}
			return 12 * 3600
		}

		return int(ttl.Seconds())
	} else {
		// Set the expiration time to 12 hours if count < 100
		_ = global.RedisDb.Expire(ctx.Request.Context(), countKey, 12*time.Hour).Err()
	}
	return 0
}

func checkIpLimit(ctx *gin.Context) int {
	ip := utils.GetClientIP(ctx)
	countKey := fmt.Sprintf("auth_verify_phone:%s", ip)
	count, err := global.RedisDb.Incr(ctx.Request.Context(), countKey).Result()
	if err != nil {
		zap.S().Errorw("[checkIpLimit] Error incrementing counter:", "msg", err.Error())
		return 0
	}
	if count >= 100 {
		ttl, err := global.RedisDb.TTL(ctx.Request.Context(), countKey).Result()
		if err != nil {
			zap.S().Errorw("[checkIpLimit] Error ttl:", "msg", err.Error())
			return 0
		}

		if ttl == -1 {
			// If no TTL, set the expiration time to 12 hours
			err := global.RedisDb.Expire(ctx.Request.Context(), countKey, 12*time.Hour).Err()
			if err != nil {
				zap.S().Errorw("[checkIpLimit] Error ttl expire:", "msg", err.Error())
				return 0
			}
			return 12 * 3600
		}

		return int(ttl.Seconds())
	} else {
		// Set the expiration time to 12 hours if count < 100
		_ = global.RedisDb.Expire(ctx.Request.Context(), countKey, 12*time.Hour).Err()
	}
	return 0
}

func saveCodeToDb(ctx *gin.Context, phone string, code string, existData *models.Code) bool {
	currTime := time.Now()
	currTimeStr := currTime.Format("2006-01-02 15:04:05")
	//aid := ctx.GetHeader("oaid")
	aid := utils.ParseHeaderInt(ctx, "oaid")
	history := models.CodeHistory{
		Phone:     phone,
		Code:      code,
		DeviceId:  ctx.GetHeader("deviceId"),
		Ip:        utils.GetClientIP(ctx),
		Oaid:      ctx.GetHeader("oaid"),
		CreatedAt: currTimeStr,
	}
	if err := global.DB.Create(&history).Error; err != nil {
		zap.S().Errorw("[saveCodeToDb] error inserting into code_history", "msg", err.Error())
		return false
	}

	// Prepare the data for the 'code' table
	codeData := models.Code{
		Phone:   phone,
		Code:    code,
		Aid:     aid,
		AddTime: time.Now().Unix(),
	}

	// If existData is provided, update the existing entry, otherwise insert a new one
	if existData != nil {
		// Update the existing record
		if err := global.DB.Model(&models.Code{}).Where(&models.Code{ID: existData.ID}).Updates(codeData).Error; err != nil {
			zap.S().Errorw("[saveCodeToDb] error updating code table", "msg", err.Error())
			return false
		}
	} else {
		// Insert a new record and return the inserted ID
		if err := global.DB.Create(&codeData).Error; err != nil {
			zap.S().Errorw("[saveCodeToDb] error inserting into code table", "msg", err.Error())
			return false
		}
	}
	return true
}

func addUserToRy(ctx *gin.Context, userId int32, userName string, userHeadImg string) (sdk.User, error) {
	rc := sdk.NewRongCloud(
		global.ServerConfig.RyInfo.RyKey,
		global.ServerConfig.RyInfo.RySecret,
	)
	userIdStr := fmt.Sprintf("%d", userId)
	return rc.UserRegister(
		userIdStr,
		userName,
		userHeadImg,
	)
}

func insertUserDevice(ctx *gin.Context, userId int32) bool {
	// 获取 Header
	UdModel := models.UserDevice{
		UserId:   userId,
		DeviceId: ctx.GetHeader("deviceId"),
	}

	global.DB.Create(&UdModel)
	return true
}

func insertUserTotal(ctx *gin.Context, userId int32) bool {
	// 获取 Header
	UtModel := models.UserTotal{
		UserId:  userId,
		AddTime: time.Now().Unix(),
	}

	global.DB.Create(&UtModel)
	return true
}

func insertDynamics(ctx *gin.Context, userId int32, nickName string) bool {
	jsonData, _ := json.Marshal([]string{}) // 将空数组编码为 JSON 字符串
	// 获取 Header
	dynamicsModel := models.Dynamics{
		UserId:  userId,
		Image:   string(jsonData),
		Content: fmt.Sprintf("大家好！我是%s，很高兴加入时光这个大家庭！", nickName),
		AddTime: time.Now().Unix(),
		Status:  1,
	}

	global.DB.Create(&dynamicsModel)
	return true
}

func checkIllegalWords(ctx *gin.Context, words string) bool {
	cacheIllegal, _ := global.RedisDb.SMembers(ctx.Request.Context(), "ppminganci").Result()
	names := []string{"likematchnichengminganci"}
	// 使用原始 SQL 查询
	var configs []models.Config
	sql := "select name, `value` from b_config where name IN (?) AND status = ?"
	// 执行查询并传入参数
	if err := global.DB.Raw(sql, names, 1).Scan(&configs).Error; err != nil {
		zap.S().Errorw("[checkIllegalWords] 查询数据库失败", "msg", err.Error())
		return false
	}

	// 3. 提取数据库中的非法词汇并拆分，合并到 cacheIllegal 中
	var dbIllegalWords []string
	for _, config := range configs {
		// 假设 config.Value 是逗号分隔的非法词汇字符串
		wordsList := strings.Split(config.Value, ",")
		dbIllegalWords = append(dbIllegalWords, wordsList...)
	}

	// 4. 合并 Redis 和 数据库中的非法词汇
	combinedIllegalWords := append(cacheIllegal, dbIllegalWords...)
	// 去重：可以使用 map 来实现去重
	uniqueIllegalWords := make(map[string]struct{})
	for _, word := range combinedIllegalWords {
		uniqueIllegalWords[word] = struct{}{}
	}

	// 5. 将去重后的非法词汇列表转换成字符串，方便查询
	combinedIllegalWordsStr := strings.Join(mapKeys(uniqueIllegalWords), ",")

	// 6. 判断传入的 words 是否包含在非法词汇中
	if strings.Contains(combinedIllegalWordsStr, words) {
		// 如果 words 存在于非法词汇中，返回相应的处理
		zap.S().Info("[checkIllegalWords] 发现非法词汇:", "words", words)
		// 进行非法词汇相关的处理，例如返回错误响应等
		return true
	}

	return false
}

// 辅助函数：获取 map 的键并返回一个切片
func mapKeys(m map[string]struct{}) []string {
	var keys []string
	for key := range m {
		keys = append(keys, key)
	}
	return keys
}
